/**
 * @file   a_star_normal.h
 * @brief  a star头文件
 * @encode UTF-8
 * @log    date        author      notes
 *         2021.09.01  Jimmy       first version
 */
#ifndef __A_STAR_NORMAL_H_ 
#define __A_STAR_NORMAL_H_ 

#include <vector>
#include <memory>
#include <algorithm>
#include <cstring>
#include <thread>
#include <iostream>

#include "debug_log/log.h"
#include "utils/maps.h"

namespace a_star_normal
{
    enum astar_status_t
    {
        ASS_NULL = 0,
        ASS_OK,
        ASS_WAIT,
        ASS_NOT_FOUND,
        ASS_START_TRAP,
        ASS_END_TRAP
    };
}

struct AStarNPoint
{
    Point    pos;
    uint16_t f = 0;  /* 从起点到终点的计算成本 */
    uint16_t g = 0;  /* 起点到当前点的累加成本 */
    uint16_t h = 0;  /* 当前点到终点的预计成本 */
};

struct Node
{
    uint8_t  status;    /* 1->open  2->close */
    uint8_t  offset;
    uint16_t g;
};


class AStarNormal
{
    public:
        class greater_class
        {
            public:
                bool operator()(AStarNPoint & a, AStarNPoint & b)
                {
                    return (a.f > b.f);
                }
        };

        class Heap
        {
            private:
                std::vector<AStarNPoint> mList;
            public:
                Heap()
                {
                    mList.clear();
                    make_heap(mList.begin(), mList.end(), greater_class());
                }

                ~Heap()
                {
                    mList.clear();
                }

                void clear()
                {
                    mList.clear();
                }

                void push(AStarNPoint point)
                {
                    mList.push_back(point);
                    push_heap(mList.begin(), mList.end(), greater_class());
                }

                AStarNPoint popMin()
                {
                    pop_heap(mList.begin(), mList.end(), greater_class());
                    AStarNPoint node = mList.back();
                    mList.pop_back();
                    return node;
                }

                void erase(AStarNPoint & point)
                {
                    for (std::vector<AStarNPoint>::iterator it = mList.begin(); it != mList.end(); ++it)
                    {
                        if ((*it).pos.x == point.pos.x && (*it).pos.y == point.pos.y)
                        {
                            mList.erase(it);
                            make_heap(mList.begin(), mList.end(), greater_class());
                            return;
                        }
                    }
                }

                int size(void)
                {
                    return (int)(mList.size());
                }

                bool isEmpty(void)
                {
                    return (mList.empty());
                }

        };

        class MapList
        {
            private:
                std::vector<Node> mList;
                uint16_t mWidth;
                uint16_t mHeight;
                uint16_t mCloseSize;

            public:
                MapList()
                {
                    mList.clear();
                    mWidth = mHeight = 0;
                    mCloseSize = 0;
                }

                ~MapList()
                {
                    
                }

                void init(uint16_t width, uint16_t height)
                {
                    mCloseSize = 0;
                    mWidth  = width;
                    mHeight = height;
                    unsigned int mapSize = width * height;

                    Node node;
                    node.g = 0;
                    node.offset = 0;
                    node.status = 0;
                    mList.resize(mapSize, node);    
                }

                void setG(uint16_t gVal, Point & point)
                {
                    int index = point.y * mWidth + point.x;
                    mList[index].g = gVal;
                }

                uint16_t getG(Point & point)
                {
                    int index = point.y * mWidth + point.x;
                    return (mList[index].g);
                }

                void setOffset(uint8_t off, Point & point)
                {
                    int index = point.y * mWidth + point.x;
                    mList[index].offset = off;
                }

                uint8_t getOffset(Point & point)
                {
                    int index = point.y * mWidth + point.x;
                    return (mList[index].offset);
                }

                void close(Point & point)
                {
                    int index = point.y * mWidth + point.x;
                    mList[index].status = 2;
                    mCloseSize ++;
                }

                bool isClosed(Point & point)
                {
                    int index = point.y * mWidth + point.x;
                    uint8_t ste = mList[index].status;

                    return (ste == 2);
                }

                void open(Point & point)
                {
                    int index = point.y * mWidth + point.x;
                    mList[index].status = 1;
                }

                bool isOpen(Point & point)
                {
                    int index = point.y * mWidth + point.x;
                    uint8_t ste = mList[index].status;

                    return (ste == 1);
                }

                uint16_t getCloseCount(void)
                {
                    return mCloseSize;
                }
        };

    public:
        AStarNormal();
        ~AStarNormal();

        void updateMap(const MapData & mapData);
        a_star_normal::astar_status_t pathing(Point & start, Point & dest, std::vector<Point> & path);

    private:
        std::unique_ptr<Heap>    mOpenList;
        std::unique_ptr<MapList> mMapList;

        uint16_t mWidth;
        uint16_t mHeight;
        uint16_t mMapSize;

        std::vector<uint8_t> mMapByte;

    private:
        uint16_t calcH(Point & nPoint, Point & destPoint);
        uint16_t calcG(Point & parentPoint, Point & neighborPoint);
        bool canPass(Point & point, uint8_t ref);
        bool isPointIn(Point & point, Point & minPoint, Point & maxPoint);

};


#endif	/* __A_STAR_H_ */

